summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbunnei <bunneidev@gmail.com>2023-02-19 09:38:59 +0100
committerbunnei <bunneidev@gmail.com>2023-06-03 09:05:32 +0200
commitb0f8aef057a62579825d7f71e6a10f9248817a8b (patch)
tree9e9d4330b12155113e6917bf327783383121d2db
parentandroid: native: Add support for custom Vulkan driver loading. (diff)
downloadyuzu-b0f8aef057a62579825d7f71e6a10f9248817a8b.tar
yuzu-b0f8aef057a62579825d7f71e6a10f9248817a8b.tar.gz
yuzu-b0f8aef057a62579825d7f71e6a10f9248817a8b.tar.bz2
yuzu-b0f8aef057a62579825d7f71e6a10f9248817a8b.tar.lz
yuzu-b0f8aef057a62579825d7f71e6a10f9248817a8b.tar.xz
yuzu-b0f8aef057a62579825d7f71e6a10f9248817a8b.tar.zst
yuzu-b0f8aef057a62579825d7f71e6a10f9248817a8b.zip
-rw-r--r--src/android/app/src/main/AndroidManifest.xml1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.java3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.java130
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.java45
-rw-r--r--src/android/app/src/main/res/menu/menu_game_grid.xml5
-rw-r--r--src/android/app/src/main/res/values/strings.xml10
9 files changed, 251 insertions, 3 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 88e1669cd..de4dbb948 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -27,6 +27,7 @@
android:supportsRtl="true"
android:isGame="true"
android:banner="@mipmap/ic_launcher"
+ android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true">
<activity
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java
index e56196310..c09b711fd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java
@@ -181,7 +181,7 @@ public final class NativeLibrary {
public static native void SetAppDirectory(String directory);
- public static native void SetGpuDriverParameters(String hookLibDir, String customDriverDir, String customDriverName, String fileRedirectDir);
+ public static native void InitializeGpuDriver(String hookLibDir, String customDriverDir, String customDriverName, String fileRedirectDir);
public static native boolean ReloadKeys();
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.java
index 9d4ed80b4..830d0b18c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.java
@@ -13,6 +13,7 @@ import android.os.Build;
import org.yuzu.yuzu_emu.model.GameDatabase;
import org.yuzu.yuzu_emu.utils.DocumentsTree;
import org.yuzu.yuzu_emu.utils.DirectoryInitialization;
+import org.yuzu.yuzu_emu.utils.GpuDriverHelper;
public class YuzuApplication extends Application {
public static GameDatabase databaseHelper;
@@ -43,7 +44,7 @@ public class YuzuApplication extends Application {
documentsTree = new DocumentsTree();
DirectoryInitialization.start(getApplicationContext());
-
+ GpuDriverHelper.initializeDriverParameters(getApplicationContext());
NativeLibrary.LogDeviceInfo();
// TODO(bunnei): Disable notifications until we support app suspension.
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java
index 552232bd3..d5009bc60 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java
@@ -9,6 +9,7 @@ import android.view.MenuItem;
import android.widget.Toast;
import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
@@ -22,6 +23,7 @@ import org.yuzu.yuzu_emu.utils.AddDirectoryHelper;
import org.yuzu.yuzu_emu.utils.DirectoryInitialization;
import org.yuzu.yuzu_emu.utils.FileBrowserHelper;
import org.yuzu.yuzu_emu.utils.FileUtil;
+import org.yuzu.yuzu_emu.utils.GpuDriverHelper;
import org.yuzu.yuzu_emu.utils.PicassoUtils;
import org.yuzu.yuzu_emu.utils.StartupHandler;
import org.yuzu.yuzu_emu.utils.ThemeUtil;
@@ -128,6 +130,41 @@ public final class MainActivity extends AppCompatActivity implements MainView {
MainPresenter.REQUEST_INSTALL_KEYS,
R.string.install_keys);
break;
+ case MainPresenter.REQUEST_SELECT_GPU_DRIVER:
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ // Get the driver name for the dialog message.
+ String driverName = GpuDriverHelper.getCustomDriverName();
+ if (driverName == null) {
+ driverName = getString(R.string.system_gpu_driver);
+ }
+
+ // Set the dialog message and title.
+ builder.setTitle(getString(R.string.select_gpu_driver_title));
+ builder.setMessage(driverName);
+
+ // Cancel button is a no-op.
+ builder.setNegativeButton(android.R.string.cancel, null);
+
+ // Select the default system driver.
+ builder.setPositiveButton(R.string.select_gpu_driver_default, (dialogInterface, i) ->
+ {
+ GpuDriverHelper.installDefaultDriver(this);
+ Toast.makeText(this, R.string.select_gpu_driver_use_default, Toast.LENGTH_SHORT).show();
+ });
+
+ // Use the file picker to install a custom driver.
+ builder.setNeutralButton(R.string.select_gpu_driver_install, (dialogInterface, i) -> {
+ FileBrowserHelper.openFilePicker(this,
+ MainPresenter.REQUEST_SELECT_GPU_DRIVER,
+ R.string.select_gpu_driver);
+ });
+
+ // Show the dialog.
+ AlertDialog alertDialog = builder.create();
+ alertDialog.show();
+
+ break;
}
}
@@ -163,12 +200,26 @@ public final class MainActivity extends AppCompatActivity implements MainView {
Toast.makeText(this, R.string.install_keys_success, Toast.LENGTH_SHORT).show();
refreshFragment();
} else {
- Toast.makeText(this, R.string.install_keys_failure, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.install_keys_failure, Toast.LENGTH_LONG).show();
launchFileListActivity(MainPresenter.REQUEST_INSTALL_KEYS);
}
}
}
break;
+
+ case MainPresenter.REQUEST_SELECT_GPU_DRIVER:
+ if (resultCode == MainActivity.RESULT_OK) {
+ int takeFlags = (Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ getContentResolver().takePersistableUriPermission(Uri.parse(result.getDataString()), takeFlags);
+ GpuDriverHelper.installCustomDriver(this, result.getData());
+ String driverName = GpuDriverHelper.getCustomDriverName();
+ if (driverName != null) {
+ Toast.makeText(this, getString(R.string.select_gpu_driver_install_success) + " " + driverName, Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, R.string.select_gpu_driver_error, Toast.LENGTH_LONG).show();
+ }
+ }
+ break;
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java
index 82667a98f..d2ef753bc 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java
@@ -12,6 +12,7 @@ import org.yuzu.yuzu_emu.utils.AddDirectoryHelper;
public final class MainPresenter {
public static final int REQUEST_ADD_DIRECTORY = 1;
public static final int REQUEST_INSTALL_KEYS = 2;
+ public static final int REQUEST_SELECT_GPU_DRIVER = 3;
private final MainView mView;
private String mDirToAdd;
private long mLastClickTime = 0;
@@ -51,6 +52,10 @@ public final class MainPresenter {
case R.id.button_install_keys:
launchFileListActivity(REQUEST_INSTALL_KEYS);
return true;
+
+ case R.id.button_select_gpu_driver:
+ launchFileListActivity(REQUEST_SELECT_GPU_DRIVER);
+ return true;
}
return false;
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.java
new file mode 100644
index 000000000..822a3f1f9
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.java
@@ -0,0 +1,130 @@
+package org.yuzu.yuzu_emu.utils;
+
+import android.content.Context;
+import android.net.Uri;
+
+import org.yuzu.yuzu_emu.NativeLibrary;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class GpuDriverHelper {
+ private static final String META_JSON_FILENAME = "meta.json";
+ private static final String DRIVER_INTERNAL_FILENAME = "gpu_driver.zip";
+ private static String fileRedirectionPath;
+ private static String driverInstallationPath;
+ private static String hookLibPath;
+
+ private static void unzip(String zipFilePath, String destDir) throws IOException {
+ File dir = new File(destDir);
+
+ // Create output directory if it doesn't exist
+ if (!dir.exists()) dir.mkdirs();
+
+ // Unpack the files.
+ ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath));
+ byte[] buffer = new byte[1024];
+ ZipEntry ze = zis.getNextEntry();
+ while (ze != null) {
+ String fileName = ze.getName();
+ File newFile = new File(destDir + fileName);
+ newFile.getParentFile().mkdirs();
+ FileOutputStream fos = new FileOutputStream(newFile);
+ int len;
+ while ((len = zis.read(buffer)) > 0) {
+ fos.write(buffer, 0, len);
+ }
+ fos.close();
+ zis.closeEntry();
+ ze = zis.getNextEntry();
+ }
+ zis.closeEntry();
+ }
+
+ public static void initializeDriverParameters(Context context) {
+ try {
+ // Initialize the file redirection directory.
+ fileRedirectionPath = context.getExternalFilesDir(null).getCanonicalPath() + "/gpu/vk_file_redirect/";
+
+ // Initialize the driver installation directory.
+ driverInstallationPath = context.getFilesDir().getCanonicalPath() + "/gpu_driver/";
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ // Initialize directories.
+ initializeDirectories();
+
+ // Initialize hook libraries directory.
+ hookLibPath = context.getApplicationInfo().nativeLibraryDir + "/";
+
+ // Initialize GPU driver.
+ NativeLibrary.InitializeGpuDriver(hookLibPath, driverInstallationPath, getCustomDriverLibraryName(), fileRedirectionPath);
+ }
+
+ public static void installDefaultDriver(Context context) {
+ // Removing the installed driver will result in the backend using the default system driver.
+ File driverInstallationDir = new File(driverInstallationPath);
+ deleteRecursive(driverInstallationDir);
+ initializeDriverParameters(context);
+ }
+
+ public static void installCustomDriver(Context context, Uri driverPathUri) {
+ // Revert to system default in the event the specified driver is bad.
+ installDefaultDriver(context);
+
+ // Ensure we have directories.
+ initializeDirectories();
+
+ // Copy the zip file URI into our private storage.
+ FileUtil.copyUriToInternalStorage(context, driverPathUri, driverInstallationPath, DRIVER_INTERNAL_FILENAME);
+
+ // Unzip the driver.
+ try {
+ unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ // Initialize the driver parameters.
+ initializeDriverParameters(context);
+ }
+
+ public static String getCustomDriverName() {
+ // Parse the custom driver metadata to retrieve the name.
+ GpuDriverMetadata metadata = new GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME);
+ return metadata.name;
+ }
+
+ private static String getCustomDriverLibraryName() {
+ // Parse the custom driver metadata to retrieve the library name.
+ GpuDriverMetadata metadata = new GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME);
+ return metadata.libraryName;
+ }
+
+ private static void initializeDirectories() {
+ // Ensure the file redirection directory exists.
+ File fileRedirectionDir = new File(fileRedirectionPath);
+ if (!fileRedirectionDir.exists()) {
+ fileRedirectionDir.mkdirs();
+ }
+ // Ensure the driver installation directory exists.
+ File driverInstallationDir = new File(driverInstallationPath);
+ if (!driverInstallationDir.exists()) {
+ driverInstallationDir.mkdirs();
+ }
+ }
+
+ private static void deleteRecursive(File fileOrDirectory) {
+ if (fileOrDirectory.isDirectory()) {
+ for (File child : fileOrDirectory.listFiles()) {
+ deleteRecursive(child);
+ }
+ }
+ fileOrDirectory.delete();
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.java
new file mode 100644
index 000000000..d2bb2dd23
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.java
@@ -0,0 +1,45 @@
+package org.yuzu.yuzu_emu.utils;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class GpuDriverMetadata {
+
+ public String name;
+ public String description;
+ public String author;
+ public String vendor;
+ public String driverVersion;
+ public int minApi;
+ public String libraryName;
+
+ public GpuDriverMetadata(String metadataFilePath) {
+ try {
+ JSONObject json = new JSONObject(getStringFromFile(metadataFilePath));
+ name = json.getString("name");
+ description = json.getString("description");
+ author = json.getString("author");
+ vendor = json.getString("vendor");
+ driverVersion = json.getString("driverVersion");
+ minApi = json.getInt("minApi");
+ libraryName = json.getString("libraryName");
+ } catch (JSONException e) {
+ // JSON is malformed, ignore and treat as unsupported metadata.
+ } catch (IOException e) {
+ // File is inaccessible, ignore and treat as unsupported metadata.
+ }
+ }
+
+ private static String getStringFromFile(String filePath) throws IOException {
+ Path path = Paths.get(filePath);
+ byte[] bytes = Files.readAllBytes(path);
+ return new String(bytes, StandardCharsets.UTF_8);
+ }
+
+}
diff --git a/src/android/app/src/main/res/menu/menu_game_grid.xml b/src/android/app/src/main/res/menu/menu_game_grid.xml
index 3eb8cf817..6211f5494 100644
--- a/src/android/app/src/main/res/menu/menu_game_grid.xml
+++ b/src/android/app/src/main/res/menu/menu_game_grid.xml
@@ -18,6 +18,11 @@
android:icon="@drawable/ic_install"
android:title="@string/install_keys"
app:showAsAction="ifRoom" />
+ <item
+ android:id="@+id/button_select_gpu_driver"
+ android:icon="@drawable/ic_settings_core"
+ android:title="@string/select_gpu_driver"
+ app:showAsAction="ifRoom" />
</menu>
</item>
<item
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index e450ee869..27749b287 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -53,6 +53,16 @@
<string name="install_keys_success">Keys successfully installed</string>
<string name="install_keys_failure">Keys file (prod.keys) is invalid</string>
+ <!-- GPU driver installation -->
+ <string name="select_gpu_driver">Select GPU driver</string>
+ <string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
+ <string name="select_gpu_driver_install">Install</string>
+ <string name="select_gpu_driver_default">Default</string>
+ <string name="select_gpu_driver_install_success">Installed</string>
+ <string name="select_gpu_driver_use_default">Using default GPU driver</string>
+ <string name="select_gpu_driver_error">Invalid driver selected, using system default!</string>
+ <string name="system_gpu_driver">System GPU driver</string>
+
<!-- Preferences Screen -->
<string name="preferences_settings">Settings</string>
<string name="preferences_general">General</string>